﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Xceed.Silverlight.Data
{
  internal static class ExpressionExtensions
  {
    internal static MemberExpression GetMember(
      this Expression source,
      string propertyName )
    {
      if( source == null )
        throw new ArgumentNullException( "source" );

      if( string.IsNullOrEmpty( propertyName ) )
        throw new ArgumentException( "propertyName cannot be null or empty.", "propertyName" );

      bool isNullableExpression;
      return ( MemberExpression )ComplexStringHelper.GetExpressionFromString( propertyName, source.Type, source, false, out isNullableExpression );
    }

    internal static BinaryExpression GreaterThan(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.GreaterThan );
    }

    internal static BinaryExpression GreaterThanOrEqual(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.GreaterThanOrEqual );
    }

    internal static BinaryExpression LessThan(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.LessThan );
    }

    internal static BinaryExpression LessThanOrEqual(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.LessThanOrEqual );
    }

    internal static BinaryExpression Equal(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.Equal );
    }

    internal static BinaryExpression NotEqual(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.BinaryComparison(
               source,
               value,
               BinaryExpression.NotEqual );
    }

    internal static BinaryExpression BinaryComparison(
      this Expression source,
      object value,
      Func<Expression, Expression, BinaryExpression> compareFunction )
    {
      if( source == null )
        throw new ArgumentNullException( "source" );

      if( compareFunction == null )
        throw new ArgumentNullException( "compareFunction" );

      Expression constant = Expression.Constant( value );

      Type sourceType = source.Type;
      Type constantType = constant.Type;

      if( sourceType == constantType )
      {
        return compareFunction.Invoke( source, constant );
      }
      else if( value == null )
      {
        if( sourceType.IsNullableType() )
          return compareFunction.Invoke( source, constant );

        Type nullableSourceType = typeof( Nullable<> ).MakeGenericType( sourceType );

        return compareFunction.Invoke(
                 Expression.Convert( source, nullableSourceType ),
                 constant );
      }
      else if( sourceType == typeof( object ) )
      {
        return compareFunction.Invoke(
                 Expression.Convert( source, constantType ),
                 constant );
      }
      else
      {
        return compareFunction.Invoke(
                 source,
                 Expression.Convert( constant, sourceType ) );
      }
    }

    internal static Expression CompareTo(
      this Expression source,
      object value )
    {
      return ExpressionExtensions.CompareTo(
               source,
               Expression.Constant( value ) );
    }

    internal static Expression CompareTo(
      this Expression source,
      Expression value )
    {
      if( source == null )
        throw new ArgumentNullException( "source" );

      if( value == null )
        throw new ArgumentNullException( "value" );

      Type sourceType = source.Type;
      Expression objectValue = value;

      if( objectValue.Type != typeof( object ) )
      {
        objectValue = Expression.Convert( objectValue, typeof( object ) );
      }

      MethodInfo compareToMethodInfo = sourceType.GetMethod(
                                         "CompareTo",
                                         new Type[] { typeof( object ) } );

      if( compareToMethodInfo == null )
      {
        Type comparableType = typeof( IComparable );

        compareToMethodInfo = comparableType.GetMethod(
                                "CompareTo",
                                new Type[] { typeof( object ) } );

        if( !comparableType.IsAssignableFrom( sourceType ) )
        {
          source = Expression.Convert( source, comparableType );
        }
      }

      return Expression.Call( source, compareToMethodInfo, objectValue );
    }
  }
}
